package demo1;

class Animal {
    public String name;
    public int age;

    public Animal(String name,int age) {
        this.name = name;
        this.age = age;
    }
    public void eat() {
        System.out.println(this.name + "正在吃...");
    }
}

class Dog extends Animal {
    public String color;
    public Dog(String name,int age,String color) {
            super(name,age);
            this.color = color;
    }
    public void eat() {
        System.out.println(this.name + "正在吃狗粮...");
    }
    public void barks() {
        System.out.println(this.name + "正在汪汪叫~~");
    }
}
class Bird extends Animal {
    public Bird(String name,int age) {
        super(name,age);
    }
    @Override
    public void eat() {
        System.out.println(this.name + "正在吃虫子....");
    }
    public void fly() {
        System.out.println(this.name + "正在飞~~");
    }
}
class B {
    public B() {
// do nothing
        func();
    }
    public void func() {
        System.out.println("B.func()");
    }
}
class D extends B {
    private int num = 1;
    @Override
    public void func() {
        System.out.println("D.func() " + num);
    }
}
// 执行结果
public class Test {
    /**
     * 多态的优缺点->demo2进行演示
     *      1.降低圈复杂度,避免使用多的if else语句
     *      2.可拓展更强->直接新建类,直接重写方法即可
     * 缺点
     *      代码运行效率降低
     *      1.属性没有多态性
     *      2.构造方法没有多态性
     */
    public static void main(String[] args) {

    }
    /**
     * 尽量避免在构造方法中调用重写方法
     * 实例化步骤
     *      1.分配内存空间
     *      2.调用合适的构造方法
     * 避免原因(构造方法也会发生多态)
     *      进行实例化时,调用子类构造方法,因为子类继承了父类,所以调用了父类构造方法
     *      父类构造方法中,调用了func方法,但是发生了动态绑定,导致执行了子类func方法
     *      其中,子类构造时,直接进入了父类构造,而父类构造又发生动态绑定,直接执行了子类方法,导致num为0
     * 结论:用尽量简单的方式使对象进入可工作状态,否则可能导致变量值有问题等难以发现的问题.
     */
    public static void main7(String[] args) {
        D d = new D();
        //输出结果:  D.func() 0
    }

    /**
     * 向上转型和向下转型
     * @param args
     */

    /**
     * 向下转型:将父类引用对象还原为子类对象,进行调用子类方法(不安全)
     *      通过强转将引用子类对象的父类引用对象,还原为子类引用的对象
     *      不安全,(例如,不是所有动物都是鸟)
     *      所以使用instanceof进行判断,原本引用对象为该子类,则进行转换,不是则不进行转换
     */
    public static void main6(String[] args) {
            Animal animal = new Bird("布谷",2);
            if(animal instanceof Bird) {
                Bird bird = (Bird)animal;
                bird.fly();
            }else {
                System.out.println("animal的类型没有引用子类对象");
            }
    }
    /**
     * 向上转型
     * @param args
     *      实际就是创建一个子类对象,将其当做父类对象来使用
     * 语法格式
     *      父类类型 父类名 = new 子类类型();
     * 使用场景
     *      1.直接赋值
     *      2.方法传参
     *      3.方法返回值
     *优缺点
     *      优点:让代码可以更简单灵活(实现多态的基础)
     *      缺点:不能调用子类特殊方法
     */
    public static void main5(String[] args) {
        Animal animal = new Dog("小灰",3,"灰色");
        animal.eat();
    }

    /**
     * 重写(覆盖):子类对父类非静态 非private修饰 非final 非构造方法的实现过程重新编写
     *      即外壳不变 核心重写
     * @param args
     * 重写规则:
     *      1.子类在重写父类方法时,返回值相同 方法名相同 方法参数相同(顺序 个数 类型)
     *      2.返回值可以不同,但是必须是父子关系
     *      3.访问修饰限定符中,子类修饰符必须大于等于父类修饰符
     *      4.父类被static final private修饰均不能重写
     *      5.构造方法不可重写
     *      6.@Override,可以对子类重写方法进行合法性检验,防止出现错误
     *
     * 什么时候使用:向上兼容,即原来方法不满足当下要求,对原来方法进行重写
     */
    public static void main4(String[] args) {
        /**
         * 动态绑定/后期绑定/晚绑定
         *      编译时,不确定方法行为,运行时,才能确定调用那个类方法
         * 静态绑定/前期绑定/早绑定
         *      编译时,根据用户传参就确定调用方法 例:函数重载
         */
    }
    /**
     * 向上转型的三种方式
     * @param args
     *  1.直接赋值
     *  2.传参
     *  3.作为返回值
     */
    public static void func1(Animal animal) {//2.传参
        /**
         * 同一引用,引用同一个方法,因为引用对象不同,所表现的行为不同
         */
        animal.eat();//体现多态
    }
    public static Animal func2() {//3.作为返回值
        return new Dog("小灰",3,"灰色");
    }
    public static void main3(String[] args) {
        Dog dog = new Dog("小黑",2,"黑色");
        func1(dog);

        Bird bird = new Bird("布谷",2);
        func1(bird);

    }
    /**
     * 理解多态的两个前提
     * @param args
     * 向上转型: 父类引用,引用了子类引用的对象
     * 重写:父子类均存在该方法,该方法内容不同
     *      1.返回类型相同
     *      2.方法名相同
     *      3.方法参数相同(参数顺序 参数个数 参数类型)
     *  向上转型 + 重写会发生动态绑定
     *          动态绑定:编译时,引用调用父类方法,但运行时,引用却调用了子类方法
     *          静态绑定:编译时,已经确定了调用那个方法
     */
    public static void main2(String[] args) {
        Animal animal = new Dog("小黑",2,"黑色");
        animal.eat();
    }
    public static void main1(String[] args) {
        Dog dog = new Dog("小黑",2,"黑色");

        dog.eat();
        dog.barks();
    }
}
